#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include "CIpodLog.h"
#include "CIpodChip.h"
#include <pthread.h>


#include <sys/time.h>







#ifdef _I2C_CHIP_20B
#define CHIP_SLAVE  0x11
#else
#define CHIP_SLAVE  0x10
#endif

#if( defined( MFI_AUTH_DEVICE_PATH ) )
#define kMFiAuthDevicePath      MFI_AUTH_DEVICE_PATH
#else
#define kMFiAuthDevicePath                      "/dev/i2c-0"
#endif


#if( defined( MFI_AUTH_DEVICE_PATH ) )
#define kMFiAuthDeviceAddress                   MFI_AUTH_DEVICE_ADDRESS
#else
#define kMFiAuthDeviceAddress                   0x10
#endif


#define kMFiAuthRetryDelayMics                  5000 // 5 ms.

#define kMFiAuthReg_ProtocolMajorVersion        0x02
#define kMFiAuthReg_AuthControlStatus           0x10
#define kMFiAuthFlagError                       0x80
#define kMFiAuthControl_GenerateSignature       1
#define kMFiAuthReg_SignatureSize               0x11
#define kMFiAuthReg_SignatureData               0x12
#define kMFiAuthReg_ChallengeSize               0x20
#define kMFiAuthReg_ChallengeData               0x21
#define kMFiAuthReg_DeviceCertificateSize       0x30
#define kMFiAuthReg_DeviceCertificateData1      0x31 // Note: auto-increments so next read is Data2, Data3, etc.

static uint8_t*         gMFiCertificatePtr = NULL;
static size_t           gMFiCertificateLen = 0;

static uint8_t*         gAuthenticationSerialNumber = NULL;


extern pthread_mutex_t authMutex;

int CIpodChip::s_ChipFd = -1;

int CIpodChip::init() {
    int ret = -1;
    char* chipPath = getenv("IPOD_ACCESSORY_CHIP_I2C_STR");
    if (chipPath == NULL) {
        CIpodLog::e("CIpodChip::init, chip path is null!\n");
        return -1;
    }

    CIpodLog::i("CIpodChip::init, chipPath %s CHIP_SLAVE = %d\n", chipPath, CHIP_SLAVE);
    if ((s_ChipFd = open(chipPath, O_RDWR)) < 0) {
        CIpodLog::e("CIpodChip::init, chip open failed! chipPath is %s.\n", chipPath);
        return -1;
    }
    ret = ioctl(s_ChipFd, I2C_TENBIT, 0);           //not 10bit
    ret = ioctl(s_ChipFd, I2C_SLAVE, CHIP_SLAVE);   //设置I2C从设备地址[6:0]
    if (ret == -1) {
        CIpodLog::e("CIpodChip::init, chip init failed!\n");
    }
    else {
        CIpodLog::i("CIpodChip::init, chip init success! s_ChipFd = %d\n", s_ChipFd);
    }
    return ret;
}

int CIpodChip::deinit() {
    int ret = -1;
    if (s_ChipFd != -1) {
        ret = close(s_ChipFd);
        s_ChipFd = -1;
        CIpodLog::i("CIpodChip::deinit, chip close! ret = %d\n", ret);
    }
    CIpodLog::i("CIpodChip::deinit, exit ok!\n");
    return ret;
}

int CIpodChip::send(uint8 regvalue, const uint8* buf, uint16 len) {
    int ret = -1;
    if (s_ChipFd != -1) {
        uint8* writebuf = (uint8*) malloc(len + 1);
        *writebuf = regvalue;
        memcpy(writebuf + 1, buf, len);
        ret = write(s_ChipFd, writebuf, len + 1);
        if (ret == -1) {
            ret = write(s_ChipFd, writebuf, len + 1);
        }
        if (ret == -1) {
            CIpodLog::e("CIpodChip::send, (reg = 0x%x) ERROR(errno = %d) !\n", regvalue, errno);
        }
        if (writebuf != NULL) {
            free(writebuf);
        }
        else {
            CIpodLog::e("CIpodChip::send, chip writebuf is null!\n");
        }
        usleep(1000);
    }
    else {
        CIpodLog::i("CIpodChip::send, chip has not been opened!\n");
    }
    return ret;
}

int CIpodChip::recv(uint8 regvalue, uint8* buf, uint16 len) {
    int res = -1;

    if (s_ChipFd == -1) {
        CIpodLog::i("CIpodChip::recv, chip has not been opened!\n");
        return -1;
    }
    res = write(s_ChipFd, &regvalue, 1);
    if (res == -1) {
        usleep(2000);
        if (s_ChipFd == -1) {
            return -1;
        }
        res = write(s_ChipFd, &regvalue, 1);
    }

    if (res == -1) {
        CIpodLog::e("CIpodChip::recv, (reg = 0x%x) ERROR(errno = %d) !\n", regvalue, errno);
        return -1;
    }

    usleep(3000);

    for (int i = 0; i <= len/256; i++) {
        if (s_ChipFd == -1) {
            return -1;
        }
        res = read(s_ChipFd, buf + i * 256, (i == len/256)?(len - i * 256):256);
    }

    if (res == -1) {
        CIpodLog::e("CIpodChip::send, (reg = 0x%x) ERROR(errno = %d) !\n", regvalue, errno);
        return -1;
    }

    usleep(1000);
    return res;
}

//buf size is too big and only read 256 byte
#if 0
int CIpodChip::recv(uint8 regvalue, uint8* buf, uint16 len) {
    int ret = -1;
    if (s_ChipFd != -1) {
        ret = write(s_ChipFd, &regvalue, 1);
        if (ret == -1) {
            usleep(2000);
            ret = write(s_ChipFd, &regvalue, 1);
        }
        if (ret == -1) {
            CIpodLog::e("CIpodChip::recv, send (reg = 0x%x) ERROR(errno = %d) !\n", regvalue, errno);
        }
        else {
            usleep(3000);
            ret = read(s_ChipFd, buf, len);
            if (ret == -1) {
                CIpodLog::e("CIpodChip::recv, recv (reg = 0x%x) ERROR(errno = %d) !\n", regvalue, errno);
            }
            usleep(1000);
        }
    }
    else {
        CIpodLog::i("CIpodChip::recv, chip has not been opened!\n");
    }
    return ret;
}
#endif

static void
iAP2_DoI2C(
    int             inFD,
    uint8_t         inRegister,
    const uint8_t*  inWritePtr,
    size_t          inWriteLen,
    uint8_t*        inReadBuf,
    size_t          inReadLen) {
    int             tries;
    int             temp;
    ssize_t         n;
    uint8_t         buf[ 1 + inWriteLen ];
    size_t          len;

    if (inReadBuf) {
        // Combined mode transactions are not supported so set the register and do a separate read.

        // 写寄存器地址, 1 个字节
        for (tries = 1;;  tries++) {
            n = write(inFD, &inRegister, 1);
            if (n == 1) {
                break;
            }
            usleep(kMFiAuthRetryDelayMics);
            if (tries > 400) {
                CIpodLog::e("MFI 写寄存器失败  地址 = %d ,重试%d次。\n", inRegister, tries);
                break;
            }
        }

        // 开始读
        n = 0;
        for (tries = 1;; tries++) {
            temp = read(inFD, inReadBuf, inReadLen);
            if (temp == inReadLen) {
                break;
            }
            usleep(kMFiAuthRetryDelayMics);
            if (tries > 400) {
                CIpodLog::e("MFI 写寄存器失败  地址 = %d ,重试%d次。\n", inRegister, tries);
                break;
            }
        }
    }
    else {
        // Gather the register and data so it can be done as a single write transaction.

        buf[ 0 ] = inRegister;
        memcpy(&buf[ 1 ], inWritePtr, inWriteLen);
        len = 1 + inWriteLen;

        for (tries = 1; ; tries++) {
            n = write(inFD, buf, len);
            if (n == (ssize_t) len) {
                break;
            }
            usleep(kMFiAuthRetryDelayMics);
            if (tries > 400) {
                CIpodLog::e("MFI 写寄存器失败  地址 = %d ,重试%d次。\n", inRegister, tries);
                break;
            }
        }
    }
}


void CIpodChip::debugHexBuf(const char* tag, const uint8* data, uint16 len) {
    uint16 debugLen = len;

#ifdef __ANDROID__
    std::string buf;
    buf.append(tag);
    buf.append(" ---- ");
    for (uint16 i = 0; i < debugLen; i++) {
        char temp[10];
        sprintf(temp, "%02x ", data[i]);
        buf.append(temp);
    }
    buf.append("\n");
    CIpodLog::d("%s", buf.data());
#else
    CIpodLog::d("%s ---- ", tag);
    for (uint16 i = 0; i < debugLen; i++) {
        CIpodLog::d("%02x ", data[i]);
    }
    CIpodLog::d("\n");
#endif


}

void  CIpodChip::MFiPlatform_CopyCertificate(uint8_t** outCertificateSNPtr, uint8_t** outCertificatePtr,
                                             size_t* outCertificateLen) {
    size_t          certificateLen;
    uint8_t*        certificatePtr;
    uint8_t*        certificateSNPtr;
    int             fd = -1;
    uint8_t         buf[ 2 ];
    char*           mfiDevicePaht;
    // If the certificate has already been cached then return that as an optimization since it doesn't change.

    if (gMFiCertificateLen >= 908) {
        *outCertificateSNPtr = gAuthenticationSerialNumber;
        *outCertificatePtr = gMFiCertificatePtr;
        *outCertificateLen = gMFiCertificateLen;
        return;
    }

    pthread_mutex_lock(&authMutex);
#if 1
    fd = open(kMFiAuthDevicePath, O_RDWR);
#else
    mfiDevicePaht = handleMfiDevName();
    fd = open(mfiDevicePaht, O_RDWR);
    CIpodLog::e("MFiPlatform_CreateSignature kMFiAuthDevicePath = %s\n", mfiDevicePaht);
#endif

    ioctl(fd, I2C_SLAVE, kMFiAuthDeviceAddress);

    certificateSNPtr = (uint8_t*) malloc(32);

    iAP2_DoI2C(fd, 0x4E, NULL, 0, certificateSNPtr, 32);

    iAP2_DoI2C(fd, kMFiAuthReg_DeviceCertificateSize, NULL, 0, buf, 2);
    certificateLen = (buf[ 0 ] << 8) | buf[ 1 ];

    certificatePtr = (uint8_t*) malloc(certificateLen);
    if (certificatePtr == NULL) {
        CIpodLog::e("MFiPlatform_CopyCertificate  malloc( certificateLen ) Failed. \n");
    }

    memset(certificatePtr, 0, certificateLen);

    // Note: reads from the data1 register auto-increment to data2, data3, etc. registers that follow it.

    iAP2_DoI2C(fd, kMFiAuthReg_DeviceCertificateData1, NULL, 0, certificatePtr, certificateLen);

    *outCertificatePtr = certificatePtr;
    *outCertificateLen = certificateLen;
    *outCertificateSNPtr  = certificateSNPtr;

    if (fd >= 0) {
        close(fd);
    }

    pthread_mutex_unlock(&authMutex);
}

void    CIpodChip::MFiPlatform_Initialize() {
    MFiPlatform_CopyCertificate(&gAuthenticationSerialNumber, &gMFiCertificatePtr, &gMFiCertificateLen);
}

static int iAP2_UseAuth3(int inFD) {
    static int gUseAuth3 = -1;

    if (gUseAuth3 == -1) {
        uint8_t buf[1];
        iAP2_DoI2C(inFD, kMFiAuthReg_ProtocolMajorVersion, NULL, 0, buf, 1);

        gUseAuth3 = (buf[0] == 0x03 ? 1 : 0);
        CIpodLog::i("Using MFi Auth v.%d chip\n", buf[0]);
    }

    return gUseAuth3;
}

void  CIpodChip::MFiPlatform_CreateSignature(
    const uint8_t*  inDigestPtr,
    size_t          inDigestLen,
    uint8_t**       outSignaturePtr,
    size_t*         outSignatureLen) {
    int             err;
    int             fd;
    uint8_t         buf[ 32 ];
    size_t          signatureLen;
    uint8_t*        signaturePtr;

    pthread_mutex_lock(&authMutex);
    char*           mfiDevicePaht;

#if 1
    fd = open(kMFiAuthDevicePath, O_RDWR);
#else
    mfiDevicePaht = handleMfiDevName();
    fd = open(mfiDevicePaht, O_RDWR);
    CIpodLog::e("MFiPlatform_CreateSignature kMFiAuthDevicePath = %s\n", mfiDevicePaht);
#endif
    if (fd < 1) {
        err = -1;
        pthread_mutex_unlock(&authMutex);
    }

    err = ioctl(fd, I2C_SLAVE, kMFiAuthDeviceAddress);

    // Write the data to sign.
    // Note: writes to the size register auto-increment to the data register that follows it.

    iAP2_DoI2C(fd, kMFiAuthReg_ChallengeData, inDigestPtr, inDigestLen, NULL, 0);

    // Generate the signature.

    buf[ 0 ] = kMFiAuthControl_GenerateSignature;
    iAP2_DoI2C(fd, kMFiAuthReg_AuthControlStatus, buf, 1, NULL, 0);

    memset(buf, 0, sizeof(buf));
    iAP2_DoI2C(fd, kMFiAuthReg_AuthControlStatus, NULL, 0, buf, 1);

    // Read the signature.
    memset(buf, 0, sizeof(buf));
    iAP2_DoI2C(fd, kMFiAuthReg_SignatureSize, NULL, 0, buf, 2);

    signatureLen = (buf[ 0 ] << 8) | buf[ 1 ];
    signaturePtr = (uint8_t*) malloc(signatureLen);
    if (signaturePtr == NULL) {
        CIpodLog::e("MFiPlatform_CreateSignature malloc( 签名长度 ) 失败。\n");
        goto exit;
    }

    memset(signaturePtr, 0, signatureLen);
    iAP2_DoI2C(fd, kMFiAuthReg_SignatureData, NULL, 0, signaturePtr, signatureLen);
    *outSignaturePtr = signaturePtr;
    *outSignatureLen = signatureLen;

exit:
    if (fd >= 0) {
        close(fd);
    }

    pthread_mutex_unlock(&authMutex);
}

